/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "wx_card.h"
#include <cstdio>
#include <mc/log.h>

namespace dev {

wx_card::wx_card() : m_quadruple_timer(nullptr), m_first_quadruple_update(true) {
    // 初始化成员变量
}

wx_card::~wx_card() {
    // 停止四元组更新定时器
    if (m_quadruple_timer) {
        m_quadruple_timer->stop();
        delete m_quadruple_timer;
        m_quadruple_timer = nullptr;
    }
}

void wx_card::init_network_ports() {
    auto objects = get_children();
    if (objects.empty()) {
        elog("Error: No network ports found");
        return;
    }
    for (auto& object : objects) {
        auto object_name = object->get_name();
        if (object_name.substr(0, object_name.find_first_of("_")) == "NicPort") {
            ilog("wx_card init network port ${name}", ("name", object_name));
            m_network_ports.push_back(static_cast<wx_port*>(object.get()));
        }
    }
}

bool wx_card::start() {
    init_network_ports();
    for (auto& port : m_network_ports) {
        port->start();
    }
    start_protocol();
    init_imu_protocol();

    // 启动四元组信息更新任务
    update_quadruple_info_async();

    return true;
}

bool wx_card::stop() {
    // 停止四元组更新定时器
    if (m_quadruple_timer) {
        m_quadruple_timer->stop();
        delete m_quadruple_timer;
        m_quadruple_timer = nullptr;
        dlog("Quadruple update timer stopped");
    }
    return true;
}

// 注册回调，框架进行动态属性替换，用于配置时的参数替换
void wx_card::replace_dynamic_property(mc::mutable_dict& object, const mc::dict& connector) {
    m_pcie_device.replace_dynamic_property(object, connector);
    m_board.replace_dynamic_property(object, connector);
}

bool wx_card::init(mc::mutable_dict& csr_object, const mc::dict& connector) {
    try {
        from_variant(csr_object, *this);
    } catch (const std::exception& e) {
        elog("wx_card init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
    return true;
}

bool wx_card::start_ncsi_protocol() {
    mctp* mctp_object =
        new mctp(this, WX_FIXED_ENDPOINT_PHY_ADDR, MCTP_MESSAGE_TYPE::MCTP_MESSAGE_TYPE_NCSI,
                 std::string(get_position()));
    auto ncsi_update_task = [this, mctp_object]() {
        m_ncsi_over_mctp_wx = std::make_shared<ncsi_over_mctp_wx>(*mctp_object);
        for (auto& port : m_network_ports) {
            port->start_ncsi_protocol(m_ncsi_over_mctp_wx, m_interval);
        }
        m_network_adapter.start_ncsi_update_task_wx(m_ncsi_over_mctp_wx, m_interval);
        m_pcie_device.start_ncsi_update_task_wx(m_ncsi_over_mctp_wx, m_interval);
        m_network_adapter_cooling.start_ncsi_update_task_wx(m_ncsi_over_mctp_wx, m_interval);
    };
    mctp_object->create_transport_and_endpoint(std::string(get_object_name()), ncsi_update_task);
    return true;
}

bool wx_card::start_protocol() {
    if (!start_ncsi_protocol()) {
        m_pcie_device.pcie_device_bdf_changed.connect(
            [this](uint8_t bus, uint8_t device, uint8_t function) {
                ilog("PCIe device bdf(${bus}:${device}:${function}) changed, start ncsi protocol",
                     ("bus", bus)("device", device)("function", function));
                start_ncsi_protocol();
            });
        return false;
    }
    return true;
}

void wx_card::update_ports_bdf_async(imu_pcie_device_req_t& pcie_device_info) {
    for (auto& port : m_network_ports) {
        bool is_success = port->update_port_bdf(pcie_device_info);
        if (!is_success) {
            elog("Error: Failed to update port(${port_id}) BDF",
                 ("port_id", static_cast<uint32_t>(port->m_network_port.PortId)));
        }
    }
}

bool wx_card::init_imu_protocol() {
    try {
        m_imu_obj = std::make_shared<imu>(get_service());
        ilog("IMU protocol initialized successfully");
        return true;
    } catch (const std::exception& e) {
        elog("Failed to initialize IMU protocol: ${error}", ("error", e.what()));
        return false;
    }
}

bool wx_card::get_pcie_vid_did_info(uint16_t& vendor_id, uint16_t& device_id) {
    if (!m_imu_obj) {
        elog("IMU protocol not initialized");
        return false;
    }

    imu_pcie_device_req_t pcie_device_info = {.system_id        = m_system_id,
                                              .socket_id        = m_pcie_device.SocketId,
                                              .bus              = m_pcie_device.DevBus,
                                              .device           = m_pcie_device.DevDevice,
                                              .function         = m_pcie_device.DevFunction,
                                              .pci_info_address = VID_DID_ADDRESS,
                                              .rlen             = 4};

    auto info = m_imu_obj->get_info_from_imu(false, pcie_device_info);
    if (!info || info->size() < 4) {
        elog("Failed to get PCI VID/DID info from IMU");
        return false;
    }

    // 解析 VID/DID (little-endian)
    device_id = (static_cast<uint16_t>((*info)[3]) << 8) | static_cast<uint16_t>((*info)[2]);
    vendor_id = (static_cast<uint16_t>((*info)[1]) << 8) | static_cast<uint16_t>((*info)[0]);

    // 检查是否为无效值
    if (vendor_id == 0xFFFF && device_id == 0xFFFF) {
        elog("Invalid VID/DID values returned from IMU");
        return false;
    }

    dlog("Successfully retrieved VID/DID: VID=0x${vid:04x}, DID=0x${did:04x}",
         ("vid", vendor_id)("did", device_id));
    return true;
}

bool wx_card::get_pcie_svid_sdid_info(uint16_t& subsystem_vendor_id,
                                      uint16_t& subsystem_device_id) {
    if (!m_imu_obj) {
        elog("IMU protocol not initialized");
        return false;
    }

    imu_pcie_device_req_t pcie_device_info = {.system_id        = m_system_id,
                                              .socket_id        = m_pcie_device.SocketId,
                                              .bus              = m_pcie_device.DevBus,
                                              .device           = m_pcie_device.DevDevice,
                                              .function         = m_pcie_device.DevFunction,
                                              .pci_info_address = SVID_SDID_ADDRESS,
                                              .rlen             = 4};

    auto info = m_imu_obj->get_info_from_imu(false, pcie_device_info);
    if (!info || info->size() < 4) {
        elog("Failed to get PCI SVID/SDID info from IMU");
        return false;
    }

    // 解析 SVID/SDID (little-endian)
    subsystem_device_id =
        (static_cast<uint16_t>((*info)[3]) << 8) | static_cast<uint16_t>((*info)[2]);
    subsystem_vendor_id =
        (static_cast<uint16_t>((*info)[1]) << 8) | static_cast<uint16_t>((*info)[0]);

    // 检查是否为无效值
    if (subsystem_vendor_id == 0xFFFF && subsystem_device_id == 0xFFFF) {
        elog("Invalid SVID/SDID values returned from IMU");
        return false;
    }

    dlog("Successfully retrieved SVID/SDID: SVID=0x${svid:04x}, SDID=0x${sdid:04x}",
         ("svid", subsystem_vendor_id)("sdid", subsystem_device_id));
    return true;
}

bool wx_card::get_network_adapter_quadruple() {
    uint16_t vendor_id, device_id, subsystem_vendor_id, subsystem_device_id;

    // 获取 VID/DID
    if (!get_pcie_vid_did_info(vendor_id, device_id)) {
        elog("Failed to retrieve VID/DID information");
        return false;
    }

    // 获取 SVID/SDID
    if (!get_pcie_svid_sdid_info(subsystem_vendor_id, subsystem_device_id)) {
        elog("Failed to retrieve SVID/SDID information");
        return false;
    }

    // 更新PCIeDevice属性
    char vid_str[16], did_str[16], svid_str[16], sdid_str[16];
    snprintf(vid_str, sizeof(vid_str), "0x%04x", vendor_id);
    snprintf(did_str, sizeof(did_str), "0x%04x", device_id);
    snprintf(svid_str, sizeof(svid_str), "0x%04x", subsystem_vendor_id);
    snprintf(sdid_str, sizeof(sdid_str), "0x%04x", subsystem_device_id);

    m_pcie_device.VendorId          = vid_str;
    m_pcie_device.DeviceId          = did_str;
    m_pcie_device.SubSystemVendorId = svid_str;
    m_pcie_device.SubSystemDeviceId = sdid_str;

    ilog("Network adapter quadruple updated: VID=${vid}, DID=${did}, SVID=${svid}, SDID=${sdid}",
         ("vid", vid_str)("did", did_str)("svid", svid_str)("sdid", sdid_str));

    return true;
}

bool wx_card::is_system_power_on() {
    try {
        auto service = get_service();
        if (!service) {
            elog("Service not available for power status check");
            return false;
        }

        mc::variant result = service->timeout_call(
            mc::milliseconds(5000), "bmc.kepler.fructrl", "/bmc/kepler/Systems/1/FruCtrl",
            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", "", mc::variants{});

        // 处理返回结果 - GetManagedObjects 返回字典格式: {path: {interface: {properties}}}
        auto managed_objects = result.as_dict();
        dlog("managed_objects: ${managed_objects}", ("managed_objects", managed_objects));

        std::string path_str = "";
        // 遍历返回的对象
        for (const auto& object_entry : managed_objects) {
            const std::string& object_path = object_entry.key.as_string();

            dlog("Object path: ${path}", ("path", object_path));

            // 检查对象路径是否包含 "Fructrl_"
            if (object_path.find("FruCtrl_") != std::string::npos) {
                path_str = object_path;
                dlog("Found FruCtrl path: ${path}", ("path", path_str));
                break;
            }
        }
        if (path_str.empty()) {
            elog("Failed to get FruCtrl path");
            return false;
        }

        // 通过timeout_call获取FruCtrl对象的PowerState属性
        mc::variant power_result =
            service->timeout_call(mc::milliseconds(5000), "bmc.kepler.fructrl",
                                  path_str,                          // 对象路径
                                  "org.freedesktop.DBus.Properties", // 接口名
                                  "Get",                             // 方法名
                                  "ss", // 参数签名：两个字符串
                                  mc::variants{
                                      std::string("bmc.kepler.Systems.FruCtrl"), // 接口名
                                      std::string("PowerState")                  // 属性名
                                  });

        if (power_result.is_string()) {
            std::string power_state = power_result.as_string();
            bool        is_on       = (power_state == "ON");
            dlog("System power state: ${state} (${is_on})",
                 ("state", power_state)("is_on", is_on ? "ON" : "OFF"));
            return is_on;
        } else {
            elog("Failed to get power state: invalid response type");
            return false;
        }
    } catch (const std::exception& e) {
        elog("Exception while checking power status: ${error}", ("error", e.what()));
        return false;
    }
}

void wx_card::on_quadruple_update_timer() {
    // 如果是首次执行，需要重新设置定时器间隔为10秒
    if (m_first_quadruple_update) {
        m_first_quadruple_update = false;
        if (m_quadruple_timer) {
            m_quadruple_timer->stop();
            m_quadruple_timer->start(m_quadruple_interval); // 改为10秒间隔
            dlog("Quadruple timer interval changed to 10 seconds");
        }
    }

    // 检查设备是否已初始化并且系统电源是否开启
    if (m_pcie_device.DevBus != 0 && is_system_power_on()) {
        bool success = get_network_adapter_quadruple();
        if (success) {
            dlog("Network adapter quadruple info updated successfully");
        } else {
            elog("Failed to update network adapter quadruple info");
        }
    } else {
        if (m_pcie_device.DevBus == 0) {
            dlog("PCIe device BDF not yet initialized, waiting...");
        } else {
            dlog("System power is off, skipping quadruple update");
        }
    }
}

void wx_card::update_quadruple_info_async() {
    // 创建定时器，延迟2分钟后开始，然后每10秒执行一次
    m_quadruple_timer = new mc::core::timer(this);
    m_quadruple_timer->timeout.connect([this]() {
        on_quadruple_update_timer();
    });

    // 设置为重复执行
    m_quadruple_timer->set_single_shot(false);

    // 延迟2分钟后开始首次执行，后续会自动每10秒执行一次
    m_quadruple_timer->start(mc::milliseconds(120000)); // 首次延迟2分钟

    dlog("Quadruple update timer started (2 minutes delay, then every 10 seconds)");
}

} // namespace dev

MC_REFLECT(dev::wx_card,
           ((m_system_id, "SystemId"))((m_pcie_device, "bmc.dev.PCIeDevice"))((m_pcie_card,
                                                                               "bmc.dev.PCIeCard"))(
               (m_network_adapter_cooling, "bmc.dev.NetworkAdapter.Cooling"))((m_network_adapter,
                                                                               "bmc.dev.NetworkAdapter"))((m_board,
                                                                               "bmc.dev.Board")))